package com.dmall.opening.up.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

import static java.lang.reflect.Modifier.isTransient;

public interface JsonType {

    default Map<String, Object> toJson() {
        return toJson((String[]) null);
    }

    default Map<String, Object> toJson(String... excluded) {
        Set<String> excludedFields = (excluded != null) ? new HashSet<>(Arrays.asList(excluded)) : Collections.emptySet();
        List<Field> fields = getFields();
        Map<String, Object> result = new HashMap<>(fields.size());
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                final String fieldName = field.getName();
                if (isTransient(field.getModifiers()) || transientField(field) || shouldExclude(excludedFields, fieldName)) {
                    continue;
                }
                final Object fieldValue = field.get(this);
                result.put(fieldName, fieldValue);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    default List<Field> getFields() {
        List<Field> fields = new ArrayList<>();
        Class<?> clazz = this.getClass();
        while (clazz != Object.class) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    default boolean transientField(Field field) {
        final Annotation[] annotations = field.getAnnotations();
        if (annotations != null) {
            for (Annotation annotation : annotations) {
                final String name = annotation.annotationType().getName();
                if ("org.springframework.beans.factory.annotation.Autowired".equals(name)
                        || "javax.annotation.Resource".equals(name)) {
                    return true;
                }
            }
        }
        return false;
    }

    default boolean shouldExclude(Set<String> excludedFields, String fieldName) {
        return !excludedFields.isEmpty() && excludedFields.contains(fieldName);
    }
}
